package com.filldream.fastboot.web.config.request;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StreamUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;

/**
 * 封装HttpServletRequest，解决了请求流的不可重复读的问题
 * 例如：POST的请求BODY不可重复度问题
 * 这里必须要一个过滤器Filter
 * 执行顺序：Filter -> HandlerInterceptor拦截器 -> AOP
 * 顺序越前性能越高
 * @author RickSun
 * @data 2023-12-08 10:11
 */
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {

    /**
     * 存储请求流数据-副本
     */
    private final byte[] body;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        //将请求流数据存储起来
        String bodyStr = getBodyString(request);
        body = bodyStr.getBytes(Charset.defaultCharset());
    }

    /**
     * 获取请求流数据
     * @param request   ServletRequest
     * @return  String
     */
    public String getBodyString(final ServletRequest request) {
        try {
            return StreamUtils.copyToString(request.getInputStream(),Charset.defaultCharset());
        } catch (IOException e) {
            log.error("RequestWrapper获取请求流数据异常:{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取请求流数据-副本
     * @return
     */
    public String getBodyString() {
        final InputStream inputStream = new ByteArrayInputStream(body);
        try {
            return StreamUtils.copyToString(inputStream, Charset.defaultCharset());
        } catch (IOException e) {
            log.error("RequestWrapper获取请求流数据-副本异常:{}", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {

            @Override
            public int read() throws IOException { return inputStream.read(); }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {}
        };
    }
}

